<?php
namespace app\modules\common\components;

use yii;
use Exception;

/**
 * 封装一层yii原生的redis操作
 * Class MyRedis
 * @package app\helpclass
 */
class RedisStore
{
    protected $redis;
    protected $prefix;

    /**
     * 具体链接的web.php配置的redis
     * MyRedis constructor.
     * @param $connection
     */
    public function __construct($connection, $prefix = '')
    {
        $this->redis = yii::$app->$connection;
    }

    /**
     * 判断key是否存在
     * @param $key
     * @return mixed
     */
    public function has($key)
    {
        return $this->redis->exists($key);
    }

    /**
     * 获取key剩余时间 Returns the time to live left for a given key, in seconds. If the key doesn't exist, FALSE is returned.
     * @param $key
     * @return int|false    the time left to live in seconds.
     */
    public function ttl($key)
    {
        return $this->redis->ttl($key);
    }

    /**
     * @param $key
     * @return int|mixed|null|string
     */
    public function get($key)
    {
        $value = $this->redis->get($this->prefix.$key);
        return ! is_null($value) ? $this->doJsonDecode($value) : null;
    }

    /**
     * @param array $keys
     * @return array
     */
    public function many(array $keys)
    {
        $results = [];

        $values = $this->redis->mget(array_map(function ($key) {
            return $this->prefix.$key;
        }, $keys));

        foreach ($values as $index => $value) {
            $results[$keys[$index]] = ! is_null($value) ? $this->doJsonDecode($value) : null;
        }

        return $results;
    }

    /**
     * @param $key
     * @param $value
     * @param $minutes
     */
    public function put($key, $value, $minutes)
    {
        $this->redis->setex(
            $this->prefix.$key,
            (int) max(1, $minutes * 60),
            $this->doJsonEncode($value)
        );
    }

    /**
     * @param array $values
     * @param $minutes
     */
    public function putMany(array $values, $minutes)
    {
        $$this->redis->multi();

        foreach ($values as $key => $value) {
            $this->put($key, $value, $minutes);
        }

        $this->redis->exec();
    }

    /**
     * @param $key
     * @param $value
     * @param $minutes
     * @return bool
     */
    public function add($key, $value, $minutes)
    {
        $lua = "return redis.call('exists',KEYS[1])<1 and redis.call('setex',KEYS[1],ARGV[2],ARGV[1])";

        return (bool) $this->redis->eval(
            $lua,
            1,
            $this->prefix.$key,
            $this->doJsonEncode($value),
            (int) max(1, $minutes * 60)
        );
    }

    /**
     * @param $key
     * @param int $value
     * @return mixed
     */
    public function increment($key, $value = 1)
    {
        return $this->redis->incrby($this->prefix.$key, $value);
    }

    /**
     * @param $key
     * @param int $value
     * @return mixed
     */
    public function decrement($key, $value = 1)
    {
        return $this->redis->decrby($this->prefix.$key, $value);
    }

    /**
     * @param $key
     * @param $value
     */
    public function forever($key, $value)
    {
        $this->redis->set($this->prefix.$key, $this->doJsonEncode($value));
    }

    /**
     * @param $key
     * @return bool
     */
    public function forget($key)
    {
        return (bool) $this->redis->del($this->prefix.$key);
    }

    /**
     * @return bool
     */
    public function flush()
    {
        $this->redis->flushdb();

        return true;
    }


    /**
     * @param $value
     * @return int|string
     */
    protected function doJsonEncode($value)
    {
        return is_numeric($value) ? $value : json_encode($value);
    }

    /**
     * @param $value
     * @return int|mixed|string
     */
    protected function doJsonDecode($value)
    {
        return is_numeric($value) ? $value : json_decode($value, true);
    }
}
